package simulation;

import java.util.ArrayList;
import java.util.HashMap;
import streaming.Host;

public class RequestedPacket {

	Integer hostId; // host id
	Integer cid; //chunk id
	double timeout; // time when requests must have been answered
	HashMap<Integer, HostRequestedDiagnostic> hostsList; // list of hosts that had the packet requested
	HashMap<String, ArrayList<HostRequestedDiagnostic>> uSet; // String is the hash of the chunk sent by the peers
	Integer NeighborsNumber;
	int[] NeighborsIdList;
	boolean sentToTracker;
	
	/**
	 * Constructor.
	 * @param chunkId The id of the packet that was requested to be diagnosed.
	 * @param deadline Time limit to await for host answer.
	 */
	public RequestedPacket(Integer cid, double timeout, Host host){
		this.hostId=host.getId();
		this.setCID(cid);
		this.setTimeout(timeout);
		this.hostsList = new HashMap<Integer, HostRequestedDiagnostic>();
		this.uSet = new HashMap<String, ArrayList<HostRequestedDiagnostic>>();
		this.NeighborsNumber = host.getNumberNeighbors();
		this.NeighborsIdList = host.getNeighborIds();
		this.setSentToTracker(false);
	}

	/**
	 * Insert a host on a specified group based on the hash calculated over the chunk data sent by host
	 * @param hostId ID of the host that had the hash calculated
	 * @param hash Value of the hash calculated over the chunk data sent by host
	 */
	public void insertUSet(Integer hostIdCM, Integer hostId, String hash) {
		// Check if there is a group to the hash calculated
		if (this.getUSet().get(hash) == null){
			// If doesn't exist creates structure
			this.getUSet().put(hash, new ArrayList<HostRequestedDiagnostic>());
		}
		
		HostRequestedDiagnostic hrd = this.getHostsList().get(hostId);
		
		// Peer must not already be in the set. Check this and if the peer answered the request
		if (!this.getUSet().get(hash).contains(hrd) && !this.getHostsList().get(hostId).isAnswered()){
			// Inserts host on set with the same hash
			this.getUSet().get(hash).add(hrd);
			
			Logs.printMessage("mc_u_set_insertion", hostIdCM + " inserted in set " + hash + " peer " + hrd.getHostId());
		}
	}

	/**
	 * When timeout has reached, finalizes insertion of data, and creates a new group including everyone that haven't answered
	 */
	public void finalizeGrouping() {
		String hash = "Haven't answered"; // if host haven't answered, inserts on a specific set
		
		for (Integer key : this.getHostsList().keySet()){
			HostRequestedDiagnostic hrd = this.getHostsList().get(key);

			// If peer haven't answered
			if (!hrd.isAnswered()){
				// Check if set of unanswered was created
				if (this.getUSet().get(hash) == null){
					this.getUSet().put(hash, new ArrayList<HostRequestedDiagnostic>());
				}
				// ... adds the host.
				this.getUSet().get(hash).add(hrd);
			}
		}
	}
	
	/**
	 * Check if all hosts have answered the request for chunk with cid
	 * @return
	 */
	public boolean allAnswered(){
		boolean allAnswered = true;
		
		for (Integer key : this.getHostsList().keySet()){
			HostRequestedDiagnostic hrd = this.getHostsList().get(key);
			
			if (!hrd.answered){
				allAnswered = false;
				break;
			}
		}
			
		return allAnswered;
	}
	
	public Integer getCID() {
		return cid;
	}

	public void setCID(Integer cid) {
		this.cid = cid;
	}

	public double getTimeout() {
		return timeout;
	}

	public void setTimeout(double timeout) {
		this.timeout = timeout;
	}

	public HashMap<Integer, HostRequestedDiagnostic> getHostsList() {
		return hostsList;
	}

	public void setHostsList(HashMap<Integer, HostRequestedDiagnostic> hostsList) {
		this.hostsList = hostsList;
	}
	
	public HashMap<String, ArrayList<HostRequestedDiagnostic>> getUSet() {
		return uSet;
	}

	public String getUSetToString() {
		String aux = new String();
		aux="U_("+ hostId +","+ cid +")={ ";
		for (String hash : uSet.keySet()) {
			aux += "[" + hash + "] ";
			for (HostRequestedDiagnostic hdr : uSet.get(hash)) {
				aux += hdr.hostId + ",";
			}
		}
		aux=aux+" }.";
		return aux;
	}

	public boolean isSentToTracker() {
		return sentToTracker;
	}

	public void setSentToTracker(boolean sentToTracker) {
		this.sentToTracker = sentToTracker;
	}
	
	public int getNeighborsNumber() {
		return this.NeighborsNumber;
	}
	
	public int[] getNeighborsIdList() {
		return this.NeighborsIdList;
	}
}